Golang gin框架(golang gin框架面试题)

您所在的位置:网站首页 gorm curd Golang gin框架(golang gin框架面试题)

Golang gin框架(golang gin框架面试题)

2023-03-21 10:42| 来源: 网络整理| 查看: 265

一、Gin 介绍 Gin 是一个 Go (Golang) 编写的轻量级 http web 框架,运行速度非常快,如果你是性能和高效的追求者 Gin 最擅长的就是 Api 接口的高并发,如果项目的规模不大,业务相对简单,这个时候我们也推荐您使用 Gin Gin 的官网:https://gin-gonic.com/zh-cn/ Github 地址:https://github.com/gin-gonic/gin 二、Gin 环境搭建 要安装 Gin 软件包,需要先安装 Go 并设置 Go 工作区(demo) $ go get -u github.com/gin-gonic/ginimport "github.com/gin-gonic/gin"import "net/http"demo$ go mod init demomain.go package main import ( "github.com/gin-gonic/gin" ) func main() { // 创建一个默认的路由引擎 r := gin.Default() // 配置路由 r.GET("/", func(c *gin.Context) { c.JSON(200, gin.H{ "username": "name1", "data": "data1", }) }) // 启动 HTTP 服务,默认在 0.0.0.0:8080 启动服务 r.Run() } $ go run main.go 三、golang 程序的热加载 所谓热加载就是当我们对代码进行修改时,程序能够自动重新加载并执行,这在我们开发中 是非常便利的,可以快速进行代码测试,省去了每次手动重新编译 工具一:https://github.com/gravityblast/fresh Fresh 是一个命令行工具,可以在您每次保存 Go 或模板文件时构建和(重新)启动您的 Web 应用程序 安装fresh 库 命令窗口,输入命令 : go get github.com/pilu/fresh 进入项目的 根目录 fresh

fresh 将会自动运行项目的 main.go 热加载大功告成。

问题:使用fresh命令,总会遇到“ 不是内部或外部命令,也不是可运行的程序或批处理文件。”或者“command not found:fresh”。 解决方案: 第一步:打开任意一处终端,用go env命令查看,如果GO111MODULE=auto,将这一行改成GO111MODULE=on。 第二步:go install github.com/pilu/fresh@latest。 第三步:go mod init。 第四步:go get github.com/pilu/fresh。 紧接着直接fresh就行了。

go get github.com/codegangsta/gingin run main.go 四、Gin 框架中的路由 4.1 路由概述

路由(Routing)是由一个 URI(或者叫路径)和一个特定的 HTTP 方法(GET、POST 等) 组成的,涉及到应用如何响应客户端对某个网站节点的访问

RESTful API 是目前比较成熟的一套互联网应用程序的 API 设计理论,所以我们设计我们的路由的时候建议参考 RESTful API 指南。 在 RESTful 架构中,每个网址代表一种资源,不同的请求方式表示执行不同的操作: 请求资源 说明 GET (SELECT) 从服务器取资源 POST(CREATE) 新建资源 PUT (UPDATE) 更新资源 DELETE 删除资源 4.2 简单的路由配置 简单的路由配置(可以通过 postman 测试) 当用 GET 请求访问一个网址的时候,做什么事情: r.GET("/", func(c *gin.Context) { c.String(200, "hello world") }) 域名/news?aid=20 r.GET("/news", func(c *gin.Context) { aid := c.Query("aid") c.JSON(200, gin.H{ "news": aid, // aid = 20 }) }) map[string]interface{} 4.3 动态路由 域名/user/20 r.GET("/user/:id", func(c *gin.Context) { id := c.Param("id") c.JSON(200, gin.H{ "news": id, // id 20 }) }) 4.4 c.String() c.JSON() c.JSONP() c.XML() c.HTML() 4.4.1 返回一个字符串 r.GET("/news", func(c *gin.Context) { aid := c.Query("aid") c.String(200, "string") }) 4.4.2 返回一个 JSON 数据 r.GET("/json", func(c *gin.Context) { aid := c.Query("aid") // 方式一:自己拼接 JSON c.JSON(http.StatusOK, gin.H{ "msg": "JSON" }) }) r.GET("/structJson", func(c *gin.Context) { // 结构体方式 var msg struct { Username string `json:"username"` Msg string `json:"msg"` Age string `json:"age"` } msg.Username = "name1" msg.Msg = "msg1" msg.Age = "18" c.JSON(200, msg) }) 4.4.3 返回JSOPN r.GET("/JSONP", func(c *gin.Context) { data := map[string]interface{}{ "foo": "bar", } // /JSONP?callback=x // 将输出:x({\"foo\":\"bar\"}) c.JSONP(http.StatusOK, data) }) 4.4.4 渲染模板 r.GET("/", func(c *gin.Context) { c.HTML( http.StatusOK, "default/index.html", map[string]interface{}{ "title": "前台首页" }) }) 五、Gin HTML 模板渲染 5.1 全部模板放在一个目录里面的配置方法 我们首先在项目根目录新建 templates 文件夹,然后在文件夹中新建 index.html Document 这是一个 html 模板 {{.title}} Gin 框架中使用 c.HTML 可以渲染模板, 渲染模板前需要使用 LoadHTMLGlob() 或者LoadHTMLFiles()方法加载模板。 r.GET("/", func(c *gin.Context) { c.HTML(http.StatusOK, "default/index.html", map[string]interface{}{"title": "前台首页"}) }) r.GET("/", func(c *gin.Context) { c.HTML(http.StatusOK, "index.html", gin.H{ "title": "Main website", }) }) 5.2 模板放在不同目录里面的配置方法 5.3 gin 模板基本语法 六、静态文件服务 当我们渲染的 HTML 文件中引用了静态文件时,我们需要配置静态 web 服务 r.Static(“/static”, “./static”) 前面的/static 表示路由 后面的./static 表示路径 r := gin.Default() r.Static("/static", "./static") r.LoadHTMLGlob("templates/**/*") 七、路由详解 路由(Routing)是由一个 URI(或者叫路径)和一个特定的 HTTP 方法(GET、POST 等) 组成的,涉及到应用如何响应客户端对某个网站节点的访问。 前面章节我们给大家介绍了路由基础以及路由配置,这里我们详细给大家讲讲路由传值、路 由返回值 7.1 GET POST 以及获取 Get Post 传值 7.1.1 Get 请求传值 GET /user?uid=20&page=1 r.GET("/user", func(c *gin.Context) { uid := c.Query("uid") page := c.DefaultQuery("page", "0") c.String(200, "uid=%v page=%v", uid, page) }) 7.1.2 动态路由传值 get /user/20 r.GET("/user/:id", func(c *gin.Context) { id := c.Param("id") c.JSON(200, gin.H{ "news": id, // id 20 }) }) 7.1.3 Post 请求传值 获取 form 表单数据 c.PostFormpostman r.POST("/doAddUser", func(c *gin.Context) { username := c.PostForm("username") password := c.PostForm("password") age := c.DefaultPostForm("age", "20") c.JSON(200, gin.H{ "usernmae": username, "password": password, "age": age, }) }) 7.1.4 Get 传值绑定到结构体 /?username=zhangsan&password=123456 type Userinfo struct { Username string `form:"username" json:"user"` Password string `form:"password" json:"password"` } func main() { // 创建一个默认的路由引擎 r := gin.Default() // 配置路由 r.GET("/", func(c *gin.Context) { var userinfo Userinfo if err := c.ShouldBind(&userinfo); err == nil { c.JSON(http.StatusOK, userinfo) } else { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) } }) } {"user":"zhangsan","password":"123456"} 7.1.5 Post 传值绑定到结构体 r.POST("/doLogin", func(c *gin.Context) { var userinfo Userinfo if err := c.ShouldBind(&userinfo); err == nil { c.JSON(http.StatusOK, userinfo) } else { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) } }) 7.1.6 获取 Post Xml 数据 ctx.ShouldBindXML 我是张三 张三 type Article struct { Title string `json:"title" xml:"title"` Content string `json:"content" xml:"content"` } r.POST("/xml", func(ctx *gin.Context) { var article Article if err := ctx.ShouldBindXML(&article); err == nil { ctx.JSON(http.StatusOK, article) }else { ctx.JSON(http.StatusBadRequest, gin.H { "err": err.Error()}) } })

在这里插入图片描述

ctx.GetRawData() 7.2 路由分组 r := gin.Default() apiRouter := r.Group("/api") { apiRouter.GET("/", func(ctx *gin.Context) { ctx.String(200, "api") }) apiRouter.GET("articles", func(ctx *gin.Context) { ctx.String(200, "/api/articles") }) } 7.3 路由分离 routerrouterapiRouter.goadminRouter.go package router // apiRouter.go import "github.com/gin-gonic/gin" func ApiRouter(r *gin.Engine) { apiRouter := r.Group("/api") { apiRouter.GET("/", func(ctx *gin.Context) { ctx.String(200, "api") }) apiRouter.GET("articles", func(ctx *gin.Context) { ctx.String(200, "/api/articles") }) } } package router // adminRouter.go import ( "net/http" "github.com/gin-gonic/gin" ) func AdminRouter(r *gin.Engine) { adminRouter := r.Group("/admin") { adminRouter.GET("/", func(ctx *gin.Context) { ctx.String(200, "admin") }) adminRouter.GET("list", func(ctx *gin.Context) { ctx.String(http.StatusOK, "admin/list") }) } } main.go package main import ( "socket/router" "github.com/gin-gonic/gin" ) func main() { // 创建一个默认的路由引擎 r := gin.Default() // 引入路由模块 router.AdminRouter(r) router.ApiRouter(r) // 启动 HTTP 服务,默认在 0.0.0.0:8080 启动服务 r.Run() } 八、Gin 中自定义控制器 8.1 控制器分组 controller\api\userController.go mkdir controller cd controller mkdir api userController.go package api import "github.com/gin-gonic/gin" func UserIndex(c *gin.Context) { c.String(200, "api UserIndex") } func UserAdd(c *gin.Context) { c.String(200, "api UserAdd") } func UserList(c *gin.Context) { c.String(200, "api UserList") } func UserDelete(c *gin.Context) { c.String(200, "api UserDelete") } apiRouter.gouserController.go import ( "socket/controller/api" "github.com/gin-gonic/gin" ) func ApiRouter(r *gin.Engine) { apiRouter := r.Group("/api") { apiRouter.GET("/", ) apiRouter.GET("users", api.UserIndex) apiRouter.GET("users/:id", api.UserList) apiRouter.POST("users", api.UserAdd) apiRouter.PUT("users/:id", api.UserUpdate) apiRouter.DELETE("users/:id", api.UserDelete) } } userController.go package api import "github.com/gin-gonic/gin" type UserController struct { } func (con UserController) Index(c *gin.Context) { c.String(200, "api Index") } func (con UserController) Add(c *gin.Context) { c.String(200, "api Add") } func (con UserController) List(c *gin.Context) { c.String(200, "api List") } func (con UserController) Update(c *gin.Context) { c.String(200, "api update") } func (con UserController) Delete(c *gin.Context) { c.String(200, "api Delete") } apiRouter.go func ApiRouter(r *gin.Engine) { apiRouter := r.Group("/api") { apiRouter.GET("/") // api.UserController{} 实例化后在可以使用结构体的方法 apiRouter.GET("users", api.UserController{}.List) apiRouter.GET("users/:id", api.UserController{}.List) apiRouter.POST("users", api.UserController{}.Add) apiRouter.PUT("users/:id", api.UserController{}.Update) apiRouter.DELETE("users/:id", api.UserController{}.Delete) } } 8.2 控制器的继承

继承后就可以调用控制器里面的公共方法

apibaseController.go package api import "github.com/gin-gonic/gin" type BaseController struct{} func (con BaseController) success(c *gin.Context) { c.String(200, "success") } func (con BaseController) error(c *gin.Context) { c.String(200, "failed") } userController.go type UserController struct { // 继承BaseController BaseController } func (con UserController) Index(c *gin.Context) { // c.String(200, "api Index") con.success(c) } func (con UserController) Add(c *gin.Context) { c.String(200, "api Add") } func (con UserController) List(c *gin.Context) { c.String(200, "api List") } func (con UserController) Update(c *gin.Context) { c.String(200, "api update") } func (con UserController) Delete(c *gin.Context) { c.String(200, "api Delete") } 九、Gin 中间件

Gin 框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、 记录日志、耗时统计等 通俗的讲:中间件就是匹配路由前和匹配路由完成后执行的一系列操作

9.1 路由中间件 9.1.1 初识中间件

Gin 中的中间件必须是一个 gin.HandlerFunc 类型,配置路由的时候可以传递多个 func 回调函数。中间件要放在最后一个回调函数的前面 ,触发的方法都可以称为中间件

// 中间件函数 func InitMiddleWare(c *gin.Context) { fmt.Println("init middle ware ") } func ApiRouter(r *gin.Engine) { apiRouter := r.Group("/api") { // 中间件要放在最后一个回调函数的前面 apiRouter.GET("/", InitMiddleWare, api.UserController{}.Index) } } 9.1.2 ctx.Next()调用该请求的剩余处理程序

中间件里面加上 ctx.Next()后,c.Next()的语句后面先不执行,跳转到后面的中间件和回调函数中执行完后,才执行c.Next()后面的语句 可以让我们在路由匹配完成后执行一些操作。比如我们统计一个请求的执行时间

func InitMiddleWare(c *gin.Context) { fmt.Println("1- init middle ware ") start := time.Now().UnixNano() // 调用c.Next()请求的剩余处理程序 // c.Next()的语句后面先不执行,先跳转路由匹配的最后一个回调函数执行后, // 才执行c.Next()后面的语句 c.Next() fmt.Println("3-程序执行完成 计算时间") // 计算耗时 Go 语言中的 Since()函数保留时间值,并用于评估与实际时间的差异 end := time.Now().UnixNano() fmt.Println(end - start) } func ApiRouter(r *gin.Engine) { apiRouter := r.Group("/api") { // 中间件要放在最后一个回调函数的前面 apiRouter.GET("/", InitMiddleWare, func(ctx *gin.Context) { fmt.Println("2 - 中间件") ctx.String(200, "/api") }) } } 9.1.3 一个路由配置多个中间件的执行顺序 多个中间件执行顺序 func InitMiddleWareOne(c *gin.Context) { fmt.Println("one- init middleware start") c.Next() fmt.Println("one- init middleware end") } func InitMiddleWareTwo(c *gin.Context) { fmt.Println("Two- init middleware start") c.Next() fmt.Println("Two- init middleware end") } func ApiRouter(r *gin.Engine) { apiRouter := r.Group("/api") { // 中间件要放在最后一个回调函数的前面 apiRouter.GET("/", InitMiddleWareOne, InitMiddleWareTwo, func(ctx *gin.Context) { fmt.Println("首页") ctx.String(200, "/api") }) } } 控制台输出的结果 one- init middleware start Two- init middleware start 首页 Two- init middleware end one- init middleware end 9.1.4 ctx.Abort()了解

Abort是终止的意思,ctx.Abort()表示终止调用该请求的剩余处理程序 Abort()后,中间件后面的回调函数(包括后面的中间件)不执行了,直接执行该中间件这里面的语句

9.2 全局中间件 r.Use(中间件1,中间件2…) func InitMiddleWareOne(c *gin.Context) { fmt.Println("one- init middleware start") c.Next() fmt.Println("one- init middleware end") } func InitMiddleWareTwo(c *gin.Context) { fmt.Println("Two- init middleware start") c.Next() fmt.Println("Two- init middleware end") } func ApiRouter(r *gin.Engine) { apiRouter := r.Group("/api") apiRouter.Use(InitMiddleWareOne, InitMiddleWareTwo) { // 中间件要放在最后一个回调函数的前面 apiRouter.GET("/", func(ctx *gin.Context) { fmt.Println("首页") ctx.String(200, "/api") }) apiRouter.GET("users", api.UserController{}.Index) } } 9.3 在路由分组中配置中间件 9.4 中间件和对应控制器之间数据共享 ctx.Set("username", "张三")username, _ := ctx.Get("username")usernamev, ok := username.(string) func LoginMiddleWare(c *gin.Context) { fmt.Println("login middle ware") c.Set("username", "张三") } 控制器 func (con UserController) Index(c *gin.Context) { username, _ := c.Get("username") fmt.Println(username) // c.String(200, username) c.JSON(200, gin.H{ "username" : username, }) // con.success(c) } 中间件注意事项 gin默认中间件 gin.Default()LoggerRecoveryLoggergin.DefaultWriterGIN_MODE=releaseRecoverygin.New() goroutine c *gin.Contextc.Copy() func LoginMiddleWare(c *gin.Context) { fmt.Println("login middle ware") c.Set("username", "张三") // 定义一个goroutine统计日志 cCp := c.Copy() go func () { time.Sleep(2 * time.Second) // 用了c.Request.URL.Path 也没有问题? fmt.Println("Done in path " + cCp.Request.URL.Path) }() } 十、Gin 中自定义Model 10.1 关于Model

如果我们的应用非常简单的话,我们可以在Controller 里面处理常见的业务逻辑。但是如果 我们有一个功能想在多个控制器、或者多个模板里面复用的话,那么我们就可以把公共的功 能单独抽取出来作为一个模块(Model)。Model 是逐步抽象的过程,一般我们会在Model 里面封装一些公共的方法让不同Controller 使用,也可以在Model 中实现和数据库打交道

10.2 Model 里面封装公共的方法 model/tools.go package model import ( "crypto/md5" "fmt" "time" ) //时间戳间戳转换成日期 func UnixToDate(timestamp int) string { t := time.Unix(int64(timestamp), 0) return t.Format("2006-01-02 15:04:05") } //日期转换成时间戳2020-05-02 15:04:05 func DateToUnix(str string) int64 { template := "2006-01-02 15:04:05" t, err := time.ParseInLocation(template, str, time.Local) if err != nil { return 0 } return t.Unix() } // 获取时间戳 func GetUnix() int64 { return time.Now().Unix() } // 获取当前日期 func GetDate() string { template := "2006-01-02 15:04:05" return time.Now().Format(template) } // 获取年月日 func GetDay() string { template := "20060102" return time.Now().Format(template) } func Md5(str string) string { data := []byte(str) return fmt.Sprintf("%x\n", md5.Sum(data)) } 十一、Gin 文件上传 十二、Gin 中的Cookie 12.1 Cookie 介绍 HTTP 是无状态协议。简单地说,当你浏览了一个页面,然后转到同一个网站的另一个页 面,服务器无法认识到这是同一个浏览器在访问同一个网站。每一次的访问,都是没有任何 关系的。如果我们要实现多个页面之间共享数据的话我们就可以使用Cookie 或者Session 实 现 cookie 是存储于访问者计算机的浏览器中。可以让我们用同一个浏览器访问同一个域名 的时候共享数 12.2 Cookie 能实现的功能 保持用户登录状态 保存用户浏览的历史记录 猜你喜欢,智能推荐 电商网站的加入购物车 12.3 设置和获取Cookie ctx.Set("username")ctx.Get("username")CookieSession Cookie c.SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool) Cookie cookie, err := c.Cookie("name") Cookie -1 12.3.3 举例 func ApiRouter(r *gin.Engine) { apiRouter := r.Group("/api") { apiRouter.GET("/", func(ctx *gin.Context) { // 设置Cookie ctx.SetCookie("username", "张三", 3600, "/", "localhost", false, false) fmt.Println("首页") ctx.String(200, "/api") }) apiRouter.GET("/news", func(ctx *gin.Context) { // 获取Cookie username, _ := ctx.Cookie("username") fmt.Println(username) ctx.String(200, "/news/"+username) }) } } 12.4 多个二级域名共享cookie a.test.comb.test.comc.SetCookie("usrename", "张三", 3600, "/", ".test.com", false, true) 十三、Gin 中的Session 十四、Gin 中使用GORM 操作mysql 数据库 14.1 GORM 简单介绍 GORM 是Golang 的一个orm 框架。简单说,ORM 就是通过实例对象的语法,完成关系型 数据库的操作的技术,是"对象-关系映射"(Object/Relational Mapping) 的缩写。使用ORM 框架可以让我们更方便的操作数据库。 GORM 官方支持的数据库类型有: MySQL, PostgreSQL, SQlite, SQL Server 14.2 Gorm 特性 https://gorm.io/zh_CN/docs/index.html 14.3 Gin 中使用GORM 14.3.1 安装 如果使用go mod 管理项目的话可以忽略此步骤 go get -u gorm.io/driver/postgres go get -u gorm.io/gorm go get -u gorm.io/gorm go get -u gorm.io/driver/mysql 14.3.2Gin 中使用Gorm 连接数据库 在models 下面新建core.go ,建立数据库链接 package model import ( "fmt" "os" "os/signal" "gopkg.in/ini.v1" "gorm.io/driver/postgres" "gorm.io/gorm" ) var DB *gorm.DB var err error func init() { fmt.Println("database init start") config, err := ini.Load("./config/app.ini") if err != nil { fmt.Printf("Failed to read file: %v", err) os.Exit(1) } ip := config.Section("postgres").Key("ip").String() port := config.Section("postgres").Key("port").String() username := config.Section("postgres").Key("username").String() password := config.Section("postgres").Key("password").String() database := config.Section("postgres").Key("database").String() // dsn := "host=localhost user=postgres password=postgres dbname=study_demo port=5432 sslmode=disable TimeZone=Asia/Shanghai" dsn := fmt.Sprintf("host=%v user=%v password=%v dbname=%v port=%v sslmode=disable TimeZone=Asia/Shanghai", ip, username, password, database, port) DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{ DryRun: false, //直接运行生成sql而不执行 // Logger: logger.Default.LogMode(logger.Info), // 可以打印SQL // QueryFields: true, // 使用表的所有字段执行SQL查询 // 关闭事务,gorm默认是打开的,关闭可以提升性能 // SkipDefaultTransaction: true, }) c := make(chan os.Signal) if err != nil { fmt.Println("Connect database failed and Shutdown web server") c


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3